home *** CD-ROM | disk | FTP | other *** search
/ Power DOS 1996 July / Power DOS - July 1996.iso / sound / c_labs / devinfo / autoinit.exe / DMAW32 / DMAW32.C next >
Encoding:
C/C++ Source or Header  |  1996-02-09  |  30.3 KB  |  967 lines

  1. /****************************************************************************
  2. *                                                                           *
  3. *  (C) Copyright Creative Technology Ltd. 1994-1996. All rights reserved.   *
  4. *                                                                           *
  5. *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY    *
  6. *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE      *
  7. *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR    *
  8. *  PURPOSE.                                                                 *
  9. *                                                                           *
  10. * You have a royalty-free right to use, modify, reproduce and               *
  11. * distribute the Sample Files (and/or any modified version) in              *
  12. * any way you find useful, provided that you agree that                     *
  13. * Creative has no warranty obligations or liability for any Sample Files.   *
  14. *                                                                           *
  15. ****************************************************************************/
  16.  
  17. /*************************************************************************
  18. *
  19. *  FILE : DMAW32.C  ver 1.00
  20. *
  21. *  DMA DEMO PROGRAM FOR PLAYING WAVE FILES IN PROTECTED MODE USING
  22. *  WATCOM C COMPILER AND DOS4GW DOS EXTENDER.
  23. *
  24. *  PURPOSE      : This protected mode program demonstrates how to play a
  25. *                 .WAV file using DMA auto-init mode.
  26. *
  27. *  LIMITATIONS  : This program only supports DSP version 3.xx and above 
  28. *                 (SBPro or later)
  29. *   
  30. *  DISCLAIMER   : Although this program has been tested with
  31. *                 standard 8/16 bit PCM WAVE files, there could
  32. *                 exist some unknown bugs.
  33. *
  34. *  COMPILE      :   wcl386 /l=dos4g dmaw32.c
  35. *
  36. **************************************************************************/
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <ctype.h>
  40. #include <conio.h>
  41. #include <dos.h>
  42. #include <i86.h>
  43. #include <mem.h>
  44. #include <malloc.h>
  45. #include <graph.h>
  46.  
  47. typedef unsigned char   BYTE;
  48. typedef unsigned short  WORD;
  49. typedef unsigned long   DWORD;
  50. typedef unsigned char   UCHAR;
  51. typedef unsigned int    UINT;
  52. typedef unsigned long   ULONG;
  53.  
  54. #define BUFSIZE     (8*1024)    // two 4 KB DMA Buffers
  55. #define MONO        1
  56. #define STEREO      2
  57. #define FALSE       0
  58. #define TRUE        1
  59. #define FAIL        0
  60. #define SUCCESS     1
  61.  
  62. // ------  Programmable Interrupt Controller (PIC)  ------
  63. #define PIC1MODE        0x20    // for irq 0 - 7
  64. #define PIC1MASK        0x21
  65. #define PIC2MODE        0xA0    // for irq 8 - 15
  66. #define PIC2MASK        0xA1
  67. #define PICEOI          0x20    // End Of Interrupt
  68.  
  69. // ------  DSP Commands  ------
  70. #define dspTimeConstant         0x0040
  71. #define dspOutputSampleRate     0x0041
  72. #define dspBlockSize            0x0048
  73. #define dspTurnSpeakerON        0x00D1
  74. #define dspTurnSpeakerOFF       0x00D3
  75. #define dspExitAutoInitDMA16    0x00D9
  76. #define dspExitAutoInitDMA8     0x00DA
  77. #define dspGetDSPVersion        0x00E1
  78.  
  79. // ------  DSP port address offsets  ------
  80. #define dspMixerAddr    0x4     // Mixer register select addr
  81. #define dspMixerData    0x5     // Mixer data port addr
  82. #define dspReset        0x6     // DSP Reset addr
  83. #define dspReadData     0xA     // DSP Read Data addr
  84. #define dspWriteBuf     0xC     // DSP Write Buffer addr
  85. #define dspDataAvail    0xE     // DSP Data Available addr
  86. #define dspDMA8Ack      0xE     // 8-bit DMA intr. acknowledge
  87. #define dspDMA16Ack     0xF     // 16-bit DMA intr. acknowledge
  88. #define dspReady        0xAA
  89.  
  90. // ------ Mixer Register (common) ------
  91. #define mixMSTRVOL      0x22
  92. #define mixVOCVOL       0x04
  93. #define mixMICVOL       0x0A
  94. #define mixOUTFILTER    0x0E
  95.  
  96. // ------  DMA controller registers port addresses  ------
  97. #define DMAWRITE        0x54
  98. #define DMAREAD         0x58
  99. #define AUTO_INIT       1
  100. #define SINGLE_CYCLE    0
  101.  
  102. typedef struct { UCHAR addr, count, page, mask, mode, FF;
  103.                  } dmaportstruct;
  104.  
  105. const dmaportstruct dmaport[8] = {
  106.           {0x00, 0x01, 0x87, 0x0A, 0x0B, 0x0C},  // chan 0
  107.           {0x02, 0x03, 0x83, 0x0A, 0x0B, 0x0C},  // chan 1
  108.           {0x04, 0x05, 0x81, 0x0A, 0x0B, 0x0C},  // chan 2  DON'T USE
  109.           {0x06, 0x07, 0x82, 0x0A, 0x0B, 0x0C},  // chan 3
  110.           {0x00, 0x00, 0x00, 0xD4, 0xD6, 0xD8},  // chan 4  DON'T USE
  111.           {0xC4, 0xC6, 0x8B, 0xD4, 0xD6, 0xD8},  // chan 5
  112.           {0xC8, 0xCA, 0x89, 0xD4, 0xD6, 0xD8},  // chan 6
  113.           {0xCC, 0xCE, 0x8A, 0xD4, 0xD6, 0xD8}   // chan 7
  114.       };
  115.                 
  116. // ------ Function Prototypes ------
  117. DWORD AllocateDMABuffer(void);
  118. void * DOSMemAlloc(DWORD);
  119. void DOSMemFree(WORD);
  120. void MemLock(void *, WORD);
  121. void MemUnlock(void *, WORD);
  122. char ResetDSP(void);
  123. char InitDMADSP();
  124. void SetSBProStereo(dmaportstruct *, int, int);
  125. void SetMixer(void);
  126. void DSPout(UINT);
  127. char DSPin(UINT *);
  128. void SetISR(void ());
  129. void RestoreISR(void);
  130. void interrupt far DMAISR(void);
  131. UINT FillBuffer(UCHAR **, ULONG *);
  132. void FillPlayBuf(UCHAR *, ULONG);
  133. void Play(UINT, char);
  134. char GetBlasterEnv(void);
  135.  
  136. // ------ Global Variables ------
  137. volatile char   DMA_buf_now_playing,
  138.                 Last_buf_played;
  139. char    DMA_buf_to_fill,
  140.         End_of_data,
  141.         DMA_16bit,
  142.         Mode, HS_mode = FALSE;
  143. UINT    Intr_num, Intr_mask, Base, IRQ_num,
  144.         DMA_chan8, DMA_chan16, DSP_ver, Output_filter;
  145. UCHAR   *DMA_buffer;
  146. WORD    DMA_buffer_selector;
  147. void (__interrupt __far *IntrSave)(void);
  148.  
  149. struct WAVEHDR{ BYTE    format[4];
  150.                 DWORD   f_len;
  151.                 BYTE    wave_fmt[8];
  152.                 DWORD   fmt_len;
  153.                 WORD    fmt_tag;
  154.                 WORD    channel;
  155.                 DWORD   samples_per_sec;
  156.                 DWORD   avg_bytes_per_sec;
  157.                 WORD    blk_align;
  158.                 WORD    bits_per_sample;
  159.                 BYTE    data[4];
  160.                 DWORD   data_len;
  161.                 } wavehdr;
  162.  
  163. // ------ Program starts ------
  164. void main(int argv, char *argc[])
  165. {
  166.     FILE    *fp;
  167.     UCHAR   *data_buf;
  168.     ULONG   bytes_left_in_data_buf;
  169.     UINT    bytes_left_to_play;
  170.     int     temp;
  171.     
  172.     if(argv>1)
  173.     {
  174.         if ((fp = fopen(argc[1], "rb")) == NULL)
  175.         {
  176.             printf("\nError opening file %s -- PROGRAM TERMINATED", argc[1]);
  177.             exit(0);
  178.         }
  179.     }
  180.     else
  181.     {
  182.         printf("\nUsage : DMAW32 wav-file\n");
  183.         exit(0);
  184.     }
  185.  
  186.     _clearscreen(_GCLEARSCREEN);
  187.     printf("\tProtected Mode DMA Demo Program for playing .WAV Files\n");
  188.     printf("\t------------------------------------------------------\n");
  189.  
  190.     // ------ Verify WAV format ------
  191.     memset(&wavehdr, 0, sizeof(wavehdr));
  192.     fread(&wavehdr, 44, 1, fp);
  193.     if(Chk_hdr() == FAIL)
  194.     {
  195.         printf("Header check error - PROGRAM ABORTED\n");
  196.         fclose(fp);
  197.         exit(0);
  198.     }
  199.  
  200.     Mode = (wavehdr.channel == 1) ? MONO : STEREO;
  201.  
  202.     // ------ Allocate memory buffers ------
  203.     if( (data_buf = (UCHAR *) malloc((size_t) wavehdr.data_len)) == NULL)
  204.     {
  205.         printf("DATABUF malloc error\n");
  206.         fclose(fp);
  207.         exit(0);
  208.     }
  209.     
  210.     if(AllocateDMABuffer() == FAIL)
  211.     {
  212.         printf("MALLOC ERROR\n");
  213.         fclose(fp);
  214.         exit(0);
  215.     }
  216.  
  217.     fread(data_buf, wavehdr.data_len, 1, fp);
  218.     fclose(fp);
  219.  
  220.     // ------ Get environment settings & check for validity ------
  221.     if(GetBlasterEnv() == FAIL)
  222.     {
  223.         printf("BLASTER env. string or parameter(s) missing -- ABORTED!");
  224.         MemUnlock((void *) &DMA_buffer, BUFSIZE);
  225.         DOSMemFree(DMA_buffer_selector);
  226.         exit(0);
  227.     }
  228.  
  229.     // ------ Reset the DSP ------
  230.     if(ResetDSP() == FAIL)
  231.     {
  232.         printf("Unable to reset DSP\n");
  233.         MemUnlock((void *) &DMA_buffer, BUFSIZE);
  234.         DOSMemFree(DMA_buffer_selector);
  235.         exit(0);
  236.     }
  237.     printf("\n\nDSP version = %d\n\n",DSP_ver);
  238.  
  239.     if((DSP_ver < 4) && (wavehdr.bits_per_sample == 16))
  240.     {
  241.         printf("\n** DSP version %d.xx does not support 16-bit files.\n",
  242.                 DSP_ver);
  243.         MemUnlock((void *) &DMA_buffer, BUFSIZE);
  244.         DOSMemFree(DMA_buffer_selector);
  245.         exit(0);
  246.     }
  247.  
  248.     // ------ Mixer setting & setup new ISR ------
  249.     SetMixer();
  250.     SetISR(DMAISR);
  251.  
  252.     // ------ Initialize DMA & DSP chip ------
  253.     if(InitDMADSP() == FAIL)
  254.     {
  255.         printf("InitDMADSP() fails - PROGRAM ABORTED!\n");
  256.         MemUnlock((void *) &DMA_buffer, BUFSIZE);
  257.         DOSMemFree(DMA_buffer_selector);
  258.         exit(0);
  259.     }
  260.  
  261.     // ------ Fill 1st half buffer ------
  262.     printf("\nPlaying file >> %s\n",argc[1]);
  263.     printf("\n    Mode  = %s\n", (Mode == MONO) ? "MONO" : "STEREO");
  264.     printf("    Type  = %d-bit samples\n", (UINT) wavehdr.bits_per_sample);
  265.     printf("    Freq. = %lu Hz\n", (ULONG) wavehdr.samples_per_sec);
  266.     printf("    Size  = %lu Bytes\n", (ULONG) wavehdr.data_len);
  267.   
  268.     DMA_buf_to_fill             = 0;
  269.     End_of_data                 = FALSE;
  270.     DMA_buf_now_playing         = 0;
  271.     Last_buf_played             = FALSE;
  272.     bytes_left_in_data_buf      = wavehdr.data_len;
  273.  
  274.     bytes_left_to_play = FillBuffer(&data_buf, &bytes_left_in_data_buf);
  275.  
  276.     // ------ Begin playing ------
  277.     if(wavehdr.data_len < BUFSIZE/2)
  278.     {
  279.         Play(bytes_left_to_play, AUTO_INIT);
  280.         while(DMA_buf_now_playing == 0) ;
  281.     }
  282.     else
  283.     {
  284.         Play(bytes_left_to_play, AUTO_INIT);
  285.         FillPlayBuf(data_buf, bytes_left_in_data_buf);
  286.     }
  287.  
  288.     if(HS_mode == TRUE)    // if 8 bit high speed mode is true then reset DSP
  289.         ResetDSP();
  290.     else
  291.         DSPout((wavehdr.bits_per_sample == 8) ?     // Done playing, Halt DMA
  292.                 dspExitAutoInitDMA8 : dspExitAutoInitDMA16);
  293.                 
  294.     DSPout(dspTurnSpeakerOFF);                  // Turn OFF speaker
  295.     
  296.     if((DSP_ver < 4) && (Mode == STEREO))       // Restore original filter
  297.     {                                           // setting for SBPro & MONO.
  298.         outp(dspMixerAddr, mixOUTFILTER);
  299.         outp(dspMixerData, (int) Output_filter & 0xFD);
  300.     }
  301.     
  302.     // ------ Restore original ISR & do clean up ------
  303.     RestoreISR();
  304.     MemUnlock((void *) &DMA_buffer, BUFSIZE);
  305.     DOSMemFree(DMA_buffer_selector);
  306.  
  307.     printf("DONE\n");
  308. }// main()
  309.  
  310.  
  311. // --------------------------------------------------------
  312. // Function     : Chk_hdr()
  313. // Description  : Check for validity of wave file header
  314. // --------------------------------------------------------
  315. int Chk_hdr(void)
  316. {
  317.     if( memcmp(wavehdr.format, "RIFF", 4))
  318.     {
  319.         printf("File is not a valid .WAV file\n");
  320.         return (FAIL);
  321.     }
  322.  
  323.     if( memcmp(wavehdr.wave_fmt, "WAVEfmt ", 8))
  324.     {
  325.         printf("File is not a valid .WAV file\n");
  326.         return (FAIL);
  327.     }
  328.  
  329.     if( !((wavehdr.channel == 1) || (wavehdr.channel == 2)))
  330.     {
  331.         printf("Unknown number of channels -> %d\n", wavehdr.channel);
  332.         return (FAIL);
  333.     }
  334.  
  335.     return (SUCCESS);
  336. }//Chk_hdr()
  337.  
  338. // --------------------------------------------------------
  339. // Function     : DOSMemAlloc()
  340. // Description  : Allocate DOS memory
  341. // --------------------------------------------------------
  342. void * DOSMemAlloc(DWORD size)
  343. {
  344.     union REGS r;
  345.  
  346.     r.x.eax = 0x0100;
  347.     r.x.ebx = (size + 15) >> 4;
  348.     int386(0x31, &r, &r);
  349.     if(r.x.cflag)
  350.         return ((DWORD) 0);
  351.  
  352.     DMA_buffer_selector = r.w.dx;
  353.     return ((void *) ((r.x.eax & 0xFFFF) << 4));
  354. }//DOSMemAlloc()
  355.  
  356. // --------------------------------------------------------
  357. // Function     : DOSMemFree()
  358. // Description  : Free DOS memory
  359. // --------------------------------------------------------
  360. void DOSMemFree(WORD selector)
  361. {
  362.     union REGS r;
  363.  
  364.     r.x.eax = 0x0101;
  365.     r.w.dx = selector;
  366.     int386(0x31, &r, &r);
  367. }//DOSMemFree()
  368.  
  369. // --------------------------------------------------------
  370. // Function     : MemLock()
  371. // Description  : Lock Memory
  372. // --------------------------------------------------------
  373. void MemLock(void *mem, WORD size) 
  374. {
  375.     union REGS  r;
  376.     
  377.     r.x.eax = 0x0600;
  378.     r.w.bx = (FP_OFF(mem) & 0xFFFF0000) >> 16;
  379.     r.w.cx = FP_OFF(mem) & 0x0000FFFF;
  380.     r.w.si = 0;
  381.     r.w.di = size;
  382.     int386(0x31, &r, &r);
  383. }//MemLock()
  384.  
  385. // --------------------------------------------------------
  386. // Function     : MemUnlock
  387. // Description  : Unlock memory region
  388. // --------------------------------------------------------
  389. void MemUnlock(void *mem, WORD size) 
  390. {
  391.     union REGS  r;
  392.     
  393.     r.x.eax = 0x0601;
  394.     r.w.bx = (FP_OFF(mem) & 0xFFFF0000) >> 16;
  395.     r.w.cx = FP_OFF(mem) & 0x0000FFFF;
  396.     r.w.si = 0;
  397.     r.w.di = size;
  398.     int386(0x31, &r, &r);
  399. }//MemUnlock()
  400.  
  401. // --------------------------------------------------------
  402. // Function     : AllocateDMABuffer()
  403. // Description  : Allocate memory for the DMA buffer within
  404. //                the same memory page
  405. // --------------------------------------------------------
  406. DWORD AllocateDMABuffer(void)
  407. {
  408.     union REGS      r;
  409.     UCHAR           buffer_allocated = FALSE,
  410.                     done = FALSE;
  411.     WORD            buffer_sel[10];
  412.     int i, Index = 0;
  413.     
  414.     do
  415.     {
  416.         if(! (DMA_buffer = (UCHAR *) DOSMemAlloc(BUFSIZE) ) )
  417.             done = TRUE;
  418.         else
  419.         {   // ------ Save every memory allocation performed ------
  420.             buffer_sel[Index] = DMA_buffer_selector;
  421.             Index++;
  422.             if(Index == 10)
  423.                done = TRUE;
  424.             // ------ Check page crossing ------
  425.             if( ((FP_OFF(DMA_buffer) + BUFSIZE+15)  & 0xF0000)
  426.                 == (FP_OFF(DMA_buffer) & 0xF0000) )
  427.             {
  428.                 buffer_allocated = TRUE;
  429.                 done = TRUE;
  430.                 MemLock((void *) &DMA_buffer, BUFSIZE);
  431.             }
  432.         }
  433.     } while(!done);
  434.  
  435.     if (!buffer_allocated)
  436.         Index++;
  437.  
  438.     // ------ Free all memory blocks crossing a page boundary ------
  439.     for(i=0; i<Index-1; i++)
  440.         DOSMemFree(buffer_sel[i]);
  441.         
  442.     return ((buffer_allocated) ? SUCCESS : FAIL);
  443. }//AllocateDMABuffer()
  444.  
  445. // --------------------------------------------------------
  446. // Function     : ResetDSP()
  447. // Description  : Resets the DSP chip
  448. // --------------------------------------------------------
  449. char ResetDSP(void)
  450. {
  451.     UINT val;
  452.  
  453.     outp(Base + dspReset, (int) 1);
  454.     delay(10);
  455.     outp(Base + dspReset, (int) 0);
  456.  
  457.     // ------ Check for correct DSP ready response ------
  458.     if(DSPin(&val))
  459.         if(val==dspReady)
  460.         {
  461.             // ------ Get DSP version ------
  462.             DSPout(dspGetDSPVersion);
  463.             DSPin(&DSP_ver);
  464.         
  465.             return(SUCCESS);
  466.         }
  467.  
  468.     return (FAIL);
  469. }//ResetDSP()
  470.  
  471. // --------------------------------------------------------
  472. // Function     : InitDMADSP()
  473. // Description  : Uses the WAV header information to program
  474. //                the DMA and DSP chips.
  475. //                (Note: The DMA chip is always programmed for
  476. //                 auto-init mode.
  477. //---------------------------------------------------------
  478. char InitDMADSP(void)
  479. {
  480.     dmaportstruct dma;
  481.     int page, offset, temp;
  482.  
  483.     // ------ Get DMA setting ------        
  484.     if(wavehdr.bits_per_sample == 8)
  485.     {
  486.         DMA_16bit = FALSE;
  487.         if(DMA_chan8 != 2)
  488.             dma = dmaport[DMA_chan8];
  489.         else
  490.         {
  491.             printf("File is 8 bit -- invalid 8-bit channel");
  492.             return (FAIL);
  493.         }
  494.     }
  495.     else
  496.     {
  497.         DMA_16bit = TRUE;
  498.         if((DMA_chan16 >= 5) && (DMA_chan16 <= 7))
  499.             dma = dmaport[DMA_chan16];
  500.         else
  501.         {
  502.             printf("File is 16 bit -- invalid 16-bit channle");
  503.             return (FAIL);
  504.         }
  505.         DMA_chan16 -= 4;
  506.     }
  507.  
  508.     page = (int) (FP_OFF(DMA_buffer) >> 16);
  509.     offset = (int) (FP_OFF(DMA_buffer) & 0xFFFF);
  510.  
  511.     if((DSP_ver < 4) && (Mode == STEREO))
  512.         SetSBProStereo(&dma, page, offset);
  513.         
  514.     if(wavehdr.bits_per_sample == 8)    // Program 8 bit DMA controller
  515.     {
  516.         outp(dma.mask, (int) (DMA_chan8 | 4));           // disable DMA
  517.         outp(dma.FF, (int) 0);                          // clear flip-flop
  518.  
  519.         outp(dma.mode, (int) (DMA_chan8 | 0x58));        // 8 bit auto-init
  520.         outp(dma.count, (int) ((BUFSIZE - 1) & 0xFF));  // LO byte of count
  521.         outp(dma.count, (int) ((BUFSIZE - 1) >> 8));    // HI byte of count
  522.     }
  523.     else        // Program 16 bit DMA controller
  524.     {
  525.     // Offset for 16-bit DMA channel must be calculated differently...
  526.     // Shift Offset 1 bit right, then copy LSB of Page to MSB of Offset.
  527.         temp = page & 0x0001;
  528.         temp <<= 15;
  529.         offset >>= 1;
  530.         offset &= 0x7FFF;
  531.         offset |= temp;
  532.  
  533.         outp(dma.mask, (int) (DMA_chan16 | 4));         // disable DMA
  534.         outp(dma.FF, (int) 0);                          // clear flip-flop
  535.  
  536.         outp(dma.mode, (int) (DMA_chan16 | 0x58));      // 16 bit auto-init
  537.         outp(dma.count, (int) ((BUFSIZE/2 - 1) & 0xFF));// LO byte of count
  538.         outp(dma.count, (int) ((BUFSIZE/2 - 1) >> 8));  // HI byte of count
  539.     }
  540.  
  541.     outp(dma.page, page);                   // Physical page number
  542.     outp(dma.addr, (int) (offset & 0xFF));  // LO byte address of buffer
  543.     outp(dma.addr, (int) (offset >> 8));    // HI byte address of buffer
  544.  
  545.     // ------ DONE programming DMA, enable it ------
  546.     if(wavehdr.bits_per_sample == 8)
  547.         outp(dma.mask, DMA_chan8);
  548.     else
  549.         outp(dma.mask, DMA_chan16);
  550.  
  551.     if(DSP_ver < 4)
  552.     {   // Set transfer time constant (HI-byte only)
  553.         DSPout((UINT) dspTimeConstant);
  554.         DSPout((UINT) (256 - 1000000/(Mode*wavehdr.samples_per_sec)));
  555.     }
  556.  
  557.     DSPout(dspTurnSpeakerON);       // Turn ON speaker before doing D/A conv.
  558.     return (SUCCESS);
  559. }// InitDMADSP()
  560.  
  561. // --------------------------------------------------------
  562. // Function     : SetSBProStereo()
  563. // Description  : Set up SBPro hardware for stereo output.
  564. //                Send a silent byte to the DSP.
  565. // --------------------------------------------------------
  566. void SetSBProStereo(dmaportstruct *dma, int page, int offset)
  567. {
  568.     outp(dspMixerAddr, mixOUTFILTER);
  569.     Output_filter = (UINT) inp(dspMixerData);
  570.     outp(dspMixerData, (int) Output_filter | 0x02);
  571.  
  572.     outp(dma->mask, (int) (DMA_chan8 | 4));           // disable DMA
  573.     outp(dma->FF, (int) 0);                           // clear flip-flop
  574.  
  575.     outp(dma->mode, (int) (DMA_chan8 | 0x48));       // 8 bit single cycle
  576.     outp(dma->count, (int) 1);                       // LO byte of count
  577.     outp(dma->count, (int) 0);                       // HI byte of count
  578.  
  579.     outp(dma->page, page);                   // Physical page number
  580.     outp(dma->addr, (int) (offset & 0xFF));  // LO byte address of buffer
  581.     outp(dma->addr, (int) (offset >> 8));    // HI byte address of buffer
  582.  
  583.     outp(dma->mask, DMA_chan8);              // end DMA programming
  584.  
  585.     DMA_buf_now_playing = 0;
  586.     *DMA_buffer = 0x80;
  587.     DSPout(0x0014);                         // DSP output one silent byte
  588.     DSPout(0);
  589.     DSPout(0);
  590.  
  591.     while(DMA_buf_now_playing == 0) ;
  592.     outp(dspMixerData, (int) Output_filter | 0x22); // set filter OFF & STEREO
  593. }    
  594.  
  595. // --------------------------------------------------------
  596. // Function     : SetMixer()
  597. // Description  : Set microphone volume to zero and voice
  598. //                output to maximum.
  599. // --------------------------------------------------------
  600. void SetMixer(void)
  601. {
  602.     outp(Base + dspMixerAddr, (int) mixMICVOL);
  603.     outp(Base + dspMixerData, (int) 0x00);
  604.  
  605.     outp(Base + dspMixerAddr, (int) mixVOCVOL);
  606.     outp(Base + dspMixerData, (int) 0xFF);
  607. }//SetMixer()
  608.  
  609. // --------------------------------------------------------
  610. // Function     : Play()
  611. // Description  : Setup the playback depending on the number
  612. //                of bits per sample, MONO/STEREO & DMAMode
  613. // --------------------------------------------------------
  614. void Play(UINT bytes_left, char dma_mode)
  615. {
  616.     // ------ If BytesLeftToPlay is 1 or 0, make sure when DSPout() 
  617.     // ------ if called, the count does not wrap around when 1 sample
  618.     // ------ is subtracted ! ------
  619.     if(bytes_left <= 1 && DMA_16bit)
  620.         bytes_left = 2;
  621.     else if (bytes_left == 0 && !DMA_16bit)
  622.         bytes_left = 1;
  623.  
  624.     if(DSP_ver < 4) // SBPro (DSP ver 3.xx)
  625.     {
  626.         if (dma_mode == AUTO_INIT)
  627.         {
  628.             DSPout(dspBlockSize);
  629.             DSPout((int) ((bytes_left - 1) & 0x00FF));
  630.             DSPout((int) ((bytes_left - 1) >> 8));
  631.             // ------ set Normal/High-Speed 8 bit Auto-init ------
  632.             if((Mode == STEREO) || (wavehdr.samples_per_sec >= 23000))
  633.             {
  634.                 HS_mode = TRUE;
  635.                 DSPout(0x0090);
  636.             }
  637.             else    
  638.                 DSPout(0x001C);
  639.         }
  640.         else
  641.         {
  642.             // ------ set Normal/High-Speed 8 bit Single-cycle ------
  643.             if((Mode == STEREO) || (wavehdr.samples_per_sec >= 23000))
  644.             {
  645.                 HS_mode = TRUE;
  646.                 DSPout(0x0091);
  647.             }
  648.             else
  649.                 DSPout(0x0014);
  650.                 
  651.             DSPout((bytes_left - 1) & 0x00FF);  // LO byte size
  652.             DSPout((bytes_left - 1) >> 8);       // HI byte size
  653.         }
  654.     }
  655.     else if(DSP_ver == 4)// SB16 (DSP ver 4.xx)
  656.     {
  657.         DSPout(dspOutputSampleRate);     // DSP output transfer rate
  658.         DSPout((int) ((wavehdr.samples_per_sec & 0x0000FF00) >> 8));// HI byte
  659.         DSPout((int) (wavehdr.samples_per_sec & 0x000000FF));       // LO byte
  660.  
  661.         if(dma_mode == AUTO_INIT)    // Auto-init 8/16 bit
  662.             DSPout((wavehdr.bits_per_sample == 8) ? 0x00C6 : 0x00B6);
  663.         else                        // Single-cycle 8/16 bit
  664.             DSPout((wavehdr.bits_per_sample == 8) ? 0x00C0 : 0x00B0);
  665.  
  666.         if(wavehdr.bits_per_sample == 8)    
  667.             DSPout((Mode == MONO) ? 0x0000 : 0x0020);   // 8 bit MONO/STEREO
  668.         else
  669.             DSPout((Mode == MONO) ? 0x0010 : 0x0030);   // 16 bit MONO/STEREO
  670.  
  671.         // ------ Program number of samples to play ------
  672.         DSPout((int) ((bytes_left/(wavehdr.bits_per_sample/8) - 1) & 0x00FF));
  673.         DSPout((int) ((bytes_left/(wavehdr.bits_per_sample/8) - 1) >> 8));
  674.     }
  675.     
  676.     return;
  677. }// Play()
  678.  
  679. // --------------------------------------------------------
  680. // Function     : FillBuffer()
  681. // Description  : Fill each half of the DMA buffer
  682. // --------------------------------------------------------
  683. UINT FillBuffer(UCHAR **buffer, ULONG *bytes_left)
  684. {
  685.     UINT count;
  686.     UCHAR *bufptr;
  687.  
  688.     // ------ Set the pointer to the desired buffer to fill ------    
  689.     bufptr = DMA_buffer + ((DMA_buf_to_fill == 0) ? 0 : BUFSIZE/2);
  690.     
  691.     if(*bytes_left < BUFSIZE/2)
  692.     {   // fill last block of data and set EndOfData
  693.         memcpy(bufptr, *buffer, *bytes_left);
  694.         if(HS_mode)
  695.         {
  696.             bufptr += *bytes_left;
  697.             memset(bufptr, 0x80, BUFSIZE/2 - *bytes_left);
  698.             count = BUFSIZE/2;
  699.             if(DMA_buf_to_fill == 1)
  700.                 End_of_data = TRUE;
  701.         }
  702.         else
  703.         {
  704.             count = *bytes_left;
  705.             End_of_data = TRUE;
  706.         }
  707.         *bytes_left = 0;
  708.     }
  709.     else
  710.     {   // fill data block into buffer
  711.         memcpy(bufptr, *buffer, BUFSIZE/2);
  712.         count = BUFSIZE/2;
  713.         *buffer += BUFSIZE/2;
  714.         *bytes_left -= BUFSIZE/2;
  715.     }
  716.  
  717.     DMA_buf_to_fill ^= 1;    // Toggle to fill other half of buffer next time
  718.     
  719.     return (count);
  720. }// FillBuffer()
  721.  
  722. // --------------------------------------------------------
  723. // Function     : FillPlayBuf()
  724. // Description  : Keeps the DMA buffer filled with new data
  725. //                until end of data
  726. // --------------------------------------------------------
  727. void FillPlayBuf(UCHAR *buffer, ULONG bytes_left)
  728. {
  729.     UINT bytes_in_buffer;
  730.  
  731.     do
  732.     {   // Wait for buffer to finish play
  733.         while( DMA_buf_to_fill == DMA_buf_now_playing) ;
  734.         bytes_in_buffer = FillBuffer(&buffer, &bytes_left);
  735.         if(!HS_mode && (bytes_in_buffer < BUFSIZE/2))
  736.             Play(bytes_in_buffer, SINGLE_CYCLE);
  737.     } while (!End_of_data) ; // End_of_data set in FillBuffer()
  738.  
  739.     while(Last_buf_played == FALSE) ;    // Wait until done playing
  740.     
  741.     return;
  742. }// FillPlayBuf()
  743.  
  744. // --------------------------------------------------------
  745. // Function     : GetBlasterEnv()
  746. // Description  : Get the BLASTER environment variable for
  747. //                the I/O port address, DMA channels and
  748. //                IRQ nubmer.
  749. // --------------------------------------------------------
  750. char GetBlasterEnv(void)
  751. {
  752.   char  buffer[5],
  753.         dma_chan_found  = FALSE,
  754.         io_port_found   = FALSE,
  755.         irq_found       = FALSE,
  756.         *env_string,
  757.         save_char;
  758.  
  759.   int   digit,
  760.         i,
  761.         multiplier;
  762.  
  763.   env_string = getenv("BLASTER");
  764.  
  765.   if (env_string == NULL)
  766.     return(FAIL);
  767.  
  768.   do
  769.   {
  770.     switch(*env_string)
  771.     {
  772.       case 'A':  // I/O base port address found
  773.       case 'a':
  774.     env_string++;
  775.     for (i = 0; i < 3; i++)  // Grab the digits
  776.     {
  777.       buffer[i] = *env_string;
  778.       env_string++;
  779.     }
  780.  
  781.     // The string is in HEX, convert it to decimal
  782.     multiplier = 1;
  783.     Base = 0;
  784.     for (i -= 1; i >= 0; i--)
  785.     {
  786.       // Convert to HEX
  787.       if (buffer[i] >= '0' && buffer[i] <= '9')
  788.         digit = buffer[i] - '0';
  789.       else if (buffer[i] >= 'A' && buffer[i] <= 'F')
  790.         digit = buffer[i] - 'A' + 10;
  791.       else if (buffer[i] >= 'a' && buffer[i] <= 'f')
  792.         digit = buffer[i] - 'a' + 10;
  793.  
  794.       Base = Base + digit * multiplier;
  795.       multiplier *= 16;
  796.     }
  797.  
  798.     io_port_found = TRUE;
  799.       break;
  800.  
  801.       case 'D': // 8-bit DMA channel
  802.       case 'd':
  803.       case 'H': // 16-bit DMA channel
  804.       case 'h':
  805.     save_char = *env_string;
  806.     env_string++;
  807.     buffer[0] = *env_string;
  808.     env_string++;
  809.  
  810.     if (*env_string >= '0' && *env_string <= '9')
  811.     {
  812.       buffer[1] = *env_string; // DMA Channel No. is 2 digits
  813.       buffer[2] = NULL;
  814.       env_string++;
  815.     }
  816.     else
  817.       buffer[1] = NULL;       // DMA Channel No. is 1 digit
  818.  
  819.     if (save_char == 'D' || save_char == 'd')
  820.       DMA_chan8  = atoi(buffer);  // 8-Bit DMA channel
  821.     else
  822.       DMA_chan16 = atoi(buffer);  // 16-bit DMA channel
  823.  
  824.     dma_chan_found = TRUE;
  825.       break;
  826.  
  827.       case 'I':  // IRQ number
  828.       case 'i':
  829.     env_string++;
  830.     buffer[0] = *env_string;
  831.     env_string++;
  832.  
  833.     if (*env_string >= '0' && *env_string <= '9')
  834.     {
  835.       buffer[1] = *env_string; // IRQ No. is 2 digits
  836.       buffer[2] = NULL;
  837.       env_string++;
  838.     }
  839.     else
  840.       buffer[1] = NULL;       // IRQ No. is 1 digit
  841.  
  842.     IRQ_num  = atoi(buffer);
  843.     irq_found = TRUE;
  844.       break;
  845.  
  846.       default:
  847.     env_string++;
  848.       break;
  849.     }
  850.  
  851.   } while (*env_string != NULL);
  852.  
  853.   if (!dma_chan_found || !io_port_found || !irq_found)
  854.     return(FAIL);
  855.  
  856.   return(SUCCESS);
  857. }//GetBlasterEnv
  858.  
  859. // --------------------------------------------------------
  860. // Function     : DSPout()
  861. // Description  : Writes the value out to the DSP chip.
  862. // --------------------------------------------------------
  863. void DSPout(UINT val)
  864. {
  865.     while( inp(Base + dspWriteBuf) & 0x80); // wait for DSP ready
  866.     outp(Base + dspWriteBuf, val);          // write data
  867. }//DSPout()
  868.  
  869. // --------------------------------------------------------
  870. // Function     : DSPin()
  871. // Description  : Reads the value from the DSP chip
  872. // --------------------------------------------------------
  873. char DSPin(UINT *val)
  874. {
  875.     while( !(inp(Base + dspDataAvail) & 0x80)); // wait for available
  876.     *val = inp(Base + dspReadData);             // data
  877.  
  878.     return (SUCCESS);
  879. }//DSPin()
  880.  
  881. // --------------------------------------------------------
  882. // Function     : SetISR()
  883. // Description  : Save the original interrupt and replace
  884. //                with the new ISR
  885. // --------------------------------------------------------
  886. void SetISR(void (__interrupt __far *new_vect)())
  887. {
  888.     union REGS r;
  889.     struct SREGS sr;
  890.     
  891.     Intr_num = (IRQ_num < 8) ? (IRQ_num + 8) : (IRQ_num - 8 + 0x70);
  892.     Intr_mask = 1 << IRQ_num;
  893.  
  894.     r.x.eax = 0x3500;
  895.     r.h.al = Intr_num & 0xFF;
  896.     sr.ds = sr.es = 0;
  897.     int386x(0x21, &r, &r, &sr);
  898.     IntrSave = MK_FP(sr.es, r.x.ebx);
  899.  
  900.     r.x.eax = 0x2500;
  901.     r.h.al = Intr_num & 0xFF;
  902.     r.x.edx = FP_OFF(new_vect);
  903.     sr.ds = FP_SEG(new_vect);
  904.     sr.es = 0;
  905.     int386x(0x21, &r, &r, &sr);
  906.     
  907.     outp(PIC1MASK, inp(PIC1MASK) & ~Intr_mask);
  908.     outp(PIC2MASK, inp(PIC2MASK) & ~(Intr_mask >> 8));
  909. }//SetISR()
  910.  
  911. // --------------------------------------------------------
  912. // Function     : RestoreISR()
  913. // Description  : Restores the saved original ISR
  914. // --------------------------------------------------------
  915. void RestoreISR(void)
  916. {
  917.     union REGS r;
  918.     struct SREGS sr;
  919.     
  920.     outp(PIC1MASK, inp(PIC1MASK) | Intr_mask);
  921.     outp(PIC2MASK, inp(PIC2MASK) | (Intr_mask >> 8));
  922.  
  923.     r.x.eax = 0x2500;
  924.     r.h.al = Intr_num & 0xFF;
  925.     r.x.edx = FP_OFF(IntrSave);
  926.     sr.ds = FP_SEG(IntrSave);
  927.     sr.es = 0;
  928.     int386x(0x21, &r, &r, &sr);
  929.     
  930. }//RestoreISR()
  931.  
  932. // --------------------------------------------------------
  933. // Function     : DMAISR()
  934. // Description  : The interrupt service routine which is
  935. //                invoked every time the DSP chip completes
  936. //                playing half of the DMA buffer.
  937. // --------------------------------------------------------
  938. void __interrupt __far DMAISR(void)
  939. {
  940.     static char second_last_buffer_played = FALSE;
  941.     int IntStatus;
  942.     
  943.     if (DMA_16bit)
  944.     {   // Check for correct interrupt status 
  945.         outp(Base + dspMixerAddr, 0x82);    // selects intr. reg. in mixer
  946.         IntStatus = inp(Base + dspMixerData);
  947.         if (IntStatus & 2)
  948.             inp(Base + dspDMA16Ack);        // Acknowledge interrupt (16-bit)
  949.     }
  950.     else
  951.         inp(Base + dspDMA8Ack);           // Acknowledge interrupt (8-bit)
  952.  
  953.     if(IRQ_num > 8)                          // End of interrupt
  954.         outp(PIC2MODE, PICEOI);
  955.     outp(PIC1MODE, PICEOI);
  956.  
  957.     DMA_buf_now_playing ^= 1;                    // Toggle for next buffer to play
  958.  
  959.     if(second_last_buffer_played)
  960.         Last_buf_played = TRUE;
  961.  
  962.     if(End_of_data)
  963.         second_last_buffer_played = TRUE;
  964.  
  965.     return;
  966. }//DMAISR()
  967.